package com.fast.orm.data;

import cn.hutool.core.text.StrBuilder;
import com.fast.orm.many.AutoQueryInfo;
import com.fast.orm.many.FastJoinQueryInfo;
import com.fast.orm.many.JoinDirection;
import com.fast.orm.mapper.TableMapper;
import com.fast.orm.mapper.TableMapperUtil;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

public class DataPackage {
    private List<SelectField> selectFieldList;
    private LinkedHashMap<String, UpdateField> updateFieldMap;
    private StrBuilder from;
    private StrBuilder select;
    private StrBuilder orderBy;
    private StrBuilder groupBy;
    private List<ConditionData> whereList = new ArrayList<>();
    private StrBuilder where;
    private List<OrderByQuery> orderByList;
    private List<GroupByQuery> groupByQueryList;
    private StrBuilder limit;
    private final Map<String, Object> paramMap = new LinkedHashMap<>();
    private List<FastJoinQueryInfo> fastJoinQueryInfoList;
    private TableMapper tableMapper;
    private String tableAlias;
    private boolean isShowTableName = Boolean.FALSE;
    private Boolean logicDelete;
    private String sql;
    private Long sqlTime;
    private String customSql;
    private Object returnVal;
    private Class returnClass;
    private TableMapper returnMapper;
    private Boolean updateOverall;
    private Boolean isJoin = Boolean.FALSE;
    private List<JoinInfo> joinList;
    private long index = 0;
    private String field;
    private Way way = Way.AND;
    private Map<String, Object> params;
    private String sqlParam;
    private List<Object> insertList;
    private Integer pageNumber;
    private Integer pageSize;
    private Integer navigatePages;
    private Integer limitParam;

    public static class JoinInfo {
        private DataPackage leftDataPackage;
        private String leftField;
        private DataPackage rightDataPackage;
        private String rightField;
        private JoinDirection direction;
        private StrBuilder and;


        public JoinDirection getDirection() {
            return direction;
        }

        public void setDirection(JoinDirection direction) {
            this.direction = direction;
        }


        public String getLeftField() {
            return leftField;
        }

        public void setLeftField(String leftField) {
            this.leftField = leftField;
        }

        public String getRightField() {
            return rightField;
        }

        public DataPackage getLeftDataPackage() {
            return leftDataPackage;
        }

        public void setLeftDataPackage(DataPackage leftDataPackage) {
            this.leftDataPackage = leftDataPackage;
        }

        public DataPackage getRightDataPackage() {
            return rightDataPackage;
        }

        public void setRightDataPackage(DataPackage rightDataPackage) {
            this.rightDataPackage = rightDataPackage;
        }

        public void setRightField(String rightField) {
            this.rightField = rightField;
        }

        public StrBuilder getAnd() {
            if (and == null) {
                and = new StrBuilder();
            }
            return and;
        }

        public void setAnd(StrBuilder and) {
            this.and = and;
        }
    }


    public enum Way {
        /**
         * 条件方式
         */
        OR("or", "OR "),
        AND("and", "AND "),
        CUSTOM("custom", " ");
        public final String name;
        public final String expression;

        Way(String name, String expression) {
            this.name = name;
            this.expression = expression;
        }
    }


    public Way getWay() {
        return way;
    }

    public void setWay(Way way) {
        this.way = way;
    }

    public String getField() {
        return field;
    }

    public void setField(String field) {
        this.way = Way.AND;
        this.field = field;
    }


    public Map<String, Object> getParams() {
        return params;
    }

    public void setParams(Map<String, Object> params) {
        this.params = params;
    }

    public String getSqlParam() {
        return sqlParam;
    }

    public void setSqlParam(String sqlParam) {
        this.sqlParam = sqlParam;
    }

    public List<Object> getInsertList() {
        if (insertList == null) {
            insertList = new ArrayList<>();
        }
        return insertList;
    }

    public Integer getPageNumber() {
        return pageNumber;
    }

    public void setPageNumber(Integer pageNumber) {
        this.pageNumber = pageNumber;
    }

    public Integer getPageSize() {
        return pageSize;
    }

    public void setPageSize(Integer pageSize) {
        this.pageSize = pageSize;
    }

    public Integer getLimitParam() {
        return limitParam;
    }

    public void setLimitParam(Integer limitParam) {
        this.limitParam = limitParam;
    }

    public Integer getNavigatePages() {
        return navigatePages;
    }

    public void setNavigatePages(Integer navigatePages) {
        this.navigatePages = navigatePages;
    }


    private DataPackage() {
    }


    public static class GroupByQuery{
        private String fieldName;

        public GroupByQuery(String fieldName) {
            this.fieldName = fieldName;
        }

        public String getFieldName() {
            return fieldName;
        }
        public void setFieldName(String fieldName) {
            this.fieldName = fieldName;
        }
    }

    public static class OrderByQuery {

        /**
         * 排序字段名
         */
        private String orderByFieldName;

        /**
         * 是否降序
         */
        private OrderByType orderByType;

        public enum OrderByType {
            ASC, DESC
        }

        public OrderByQuery(String orderByFieldName, OrderByType orderByType) {
            this.orderByFieldName = orderByFieldName;
            this.orderByType = orderByType;
        }

        public String getOrderByFieldName() {
            return orderByFieldName;
        }

        public void setOrderByFieldName(String orderByFieldName) {
            this.orderByFieldName = orderByFieldName;
        }

        public OrderByType getOrderByType() {
            return orderByType;
        }

        public void setOrderByType(OrderByType orderByType) {
            this.orderByType = orderByType;
        }
    }

    public static class ConditionData {
        private String field;
        private Object value;
        private Object betweenMin;
        private Object betweenMax;
        private Way way;
        private Map<String, Object> params;
        private String sql;
        private Expression expression;
        private BaseMapper<?> baseMapper;
        private String mapperField;


        public ConditionData() {
        }

        public ConditionData(String field, Object value, Way way, Expression expression) {
            this.field = field;
            this.value = value;
            this.way = way;
            this.expression = expression;
        }

        public ConditionData(String field,BaseMapper<?> baseMapper, Way way, Expression expression) {
            this.field = field;
            this.baseMapper = baseMapper;
            this.mapperField = baseMapper.dataPackage.getField();
            this.way = way;
            this.expression = expression;
        }

        public ConditionData(String field, Object betweenMin, Object betweenMax, Way way, Expression expression) {
            this.field = field;
            this.betweenMin = betweenMin;
            this.betweenMax = betweenMax;
            this.way = way;
            this.expression = expression;
        }

        public ConditionData(String field, Way way, Expression expression) {
            this.field = field;
            this.way = way;
            this.expression = expression;
        }

        public BaseMapper<?> getBaseMapper() {
            return baseMapper;
        }

        public String getMapperField() {
            return mapperField;
        }

        public void setMapperField(String mapperField) {
            this.mapperField = mapperField;
        }

        public void setBaseMapper(BaseMapper<?> baseMapper) {
            this.baseMapper = baseMapper;
        }

        public String getField() {
            return field;
        }

        public void setField(String field) {
            this.field = field;
        }

        public Object getValue() {
            return value;
        }

        public void setValue(Object value) {
            this.value = value;
        }

        public Object getBetweenMin() {
            return betweenMin;
        }

        public void setBetweenMin(Object betweenMin) {
            this.betweenMin = betweenMin;
        }

        public Object getBetweenMax() {
            return betweenMax;
        }

        public void setBetweenMax(Object betweenMax) {
            this.betweenMax = betweenMax;
        }

        public Way getWay() {
            return way;
        }

        public void setWay(Way way) {
            this.way = way;
        }

        public Map<String, Object> getParams() {
            return params;
        }

        public void setParams(Map<String, Object> params) {
            this.params = params;
        }

        public String getSql() {
            return sql;
        }

        public void setSql(String sql) {
            this.sql = sql;
        }

        public Expression getExpression() {
            return expression;
        }

        public void setExpression(Expression expression) {
            this.expression = expression;
        }
    }


    public DataPackage(Class<?> clazz) {
        this.tableMapper = TableMapperUtil.getTableMappers(clazz);
        this.tableAlias = this.tableMapper.getTableName();
        this.returnClass = this.tableMapper.getObjClass();
        this.returnMapper = this.tableMapper;
    }

    public DataPackage(Class<?> clazz, Class<?> returnClass) {
        this.tableMapper = TableMapperUtil.getTableMappers(clazz);
        this.tableAlias = this.tableMapper.getTableName();
        this.returnClass = returnClass;
        this.returnMapper = TableMapperUtil.getTableMappers(returnClass, this.tableMapper.getTableName());
    }


    public DataPackage(Class<?> clazz, String tableAlias) {
        this.tableMapper = TableMapperUtil.getTableMappers(clazz);
        this.tableAlias = tableAlias;
        this.returnClass = this.tableMapper.getObjClass();
        this.returnMapper = this.tableMapper;
    }

    public static class UpdateField {
        private final String field;
        private final Object value;
        private final UpdateType updateType;
        private Map<String, Object> data;

        public UpdateField(String field, Object value, UpdateType updateType) {
            this.field = field;
            this.value = value;
            this.updateType = updateType;
        }

        public UpdateField(String field, Object value, UpdateType updateType, Map<String, Object> data) {
            this.field = field;
            this.value = value;
            this.updateType = updateType;
            this.data = data;
        }

        public enum UpdateType {
            /**
             * 更新方式
             */
            EQUAL(""), THIS_ADD("+"), THIS_SBU("-"), THIS_MUL("*"), THIS_DIV("/"), THIS_MODULO("%"), CUSTOMIZE("");
            public final String method;

            UpdateType(String method) {
                this.method = method;
            }
        }

        public String getField() {
            return field;
        }

        public Object getValue() {
            return value;
        }

        public UpdateType getUpdateType() {
            return updateType;
        }

        public Map<String, Object> getData() {
            return data;
        }
    }


    public static class SelectField {
        private String field;
        private SelectType selectType;

        public enum SelectType {
            FIELD, SUM, AVG, MIN, MAX, DISTINCT, DESC, ASC
        }

        public SelectField(String field, SelectType selectType) {
            this.field = field;
            this.selectType = selectType;
        }

        public String getField() {
            return field;
        }

        public void setField(String field) {
            this.field = field;
        }

        public SelectType getType() {
            return selectType;
        }

        public void setType(SelectType selectType) {
            this.selectType = selectType;
        }
    }

    public StrBuilder getSelect() {
        return select;
    }

    public void setSelect(StrBuilder select) {
        this.select = select;
    }

    public List<SelectField> getSelectFieldList() {
        if (selectFieldList == null) {
            selectFieldList = new ArrayList<>();
        }
        return selectFieldList;
    }

    public String getTableAlias() {
        return tableAlias;
    }

    public void setTableAlias(String tableAlias) {
        this.tableAlias = tableAlias;
    }


    public void showTableName() {
        isShowTableName = true;
    }

    public boolean isShowTableName() {
        return isShowTableName;
    }

    public TableMapper getTableMapper() {
        return tableMapper;
    }

    public List<ConditionData> getWhereList() {
        return whereList;
    }

    public void addWhere(ConditionData data) {
        whereList.add(data);
    }

    public List<OrderByQuery> getOrderByList() {
        if (orderByList == null) {
            orderByList = new ArrayList<>();
        }
        return orderByList;
    }

    public List<GroupByQuery> getGroupByQueryList() {
        if (groupByQueryList == null) {
            groupByQueryList = new ArrayList<>();
        }
        return groupByQueryList;
    }

    public LinkedHashMap<String, UpdateField> getUpdateFieldMap() {
        if (updateFieldMap == null) {
            updateFieldMap = new LinkedHashMap<>();
        }
        return updateFieldMap;
    }

    public Map<String, Object> getParamMap() {
        return paramMap;
    }

    public Boolean getLogicDelete() {
        return logicDelete != null ? logicDelete : tableMapper.getLogicDelete();
    }

    public void setLogicDelete(Boolean logicDelete) {
        this.logicDelete = logicDelete;
    }

    public String getSql() {
        return sql;
    }

    public void setSql(String sql) {
        this.sql = sql;
    }

    public Long getSqlTime() {
        return sqlTime;
    }

    public void setSqlTime(Long sqlTime) {
        this.sqlTime = sqlTime;
    }

    public String getCustomSql() {
        return customSql;
    }

    public void setCustomSql(String customSql) {
        this.customSql = customSql;
    }

    public Object getReturnVal() {
        return returnVal;
    }

    public void setReturnVal(Object returnVal) {
        this.returnVal = returnVal;
    }

    public void addFastJoinQueryInfoList(FastJoinQueryInfo fastJoinQueryInfo) {
        if (fastJoinQueryInfoList == null) {
            fastJoinQueryInfoList = new ArrayList<>();
        }
        fastJoinQueryInfoList.add(fastJoinQueryInfo);
    }

    public List<FastJoinQueryInfo> getFastJoinQueryInfoList() {
        return fastJoinQueryInfoList;
    }

    public StrBuilder getLimit() {
        if (limit == null) {
            limit = new StrBuilder();
        }
        return limit;
    }

    public Boolean getUpdateOverall() {
        return updateOverall;
    }

    public StrBuilder getWhere() {
        return where;
    }

    public void setWhere(StrBuilder where) {
        this.where = where;
    }

    public void setUpdateOverall(Boolean updateOverall) {
        this.updateOverall = updateOverall;
    }

    public Boolean getJoin() {
        return isJoin;
    }

    public void setJoin(Boolean join) {
        isJoin = join;
    }

    public long getIndex() {
        return ++index;
    }

    public List<JoinInfo> getJoinList() {
        if (joinList == null) {
            joinList = new ArrayList<>();
        }
        return joinList;
    }

    public StrBuilder getFrom() {
        return from;
    }

    public void setFrom(StrBuilder from) {
        this.from = from;
    }

    public Class getReturnClass() {
        return returnClass;
    }

    public void setReturnClass(Class returnClass) {
        this.returnClass = returnClass;
    }

    public TableMapper getReturnMapper() {
        return returnMapper;
    }

    public void setReturnMapper(TableMapper returnMapper) {
        this.returnMapper = returnMapper;
    }

    public StrBuilder getOrderBy() {
        return orderBy;
    }

    public void setOrderBy(StrBuilder orderBy) {
        this.orderBy = orderBy;
    }

    public StrBuilder getGroupBy() {
        return groupBy;
    }

    public void setGroupBy(StrBuilder groupBy) {
        this.groupBy = groupBy;
    }

}
